/**
 * www.easyplatform.cn ©2016
 */
package cn.easyplatform.studio.web.editors;

import cn.easyplatform.entities.BaseEntity;
import cn.easyplatform.entities.ContentEntity;
import cn.easyplatform.entities.EntityInfo;
import cn.easyplatform.entities.beans.batch.BatchBean;
import cn.easyplatform.entities.beans.list.ListBean;
import cn.easyplatform.entities.beans.page.PageBean;
import cn.easyplatform.entities.beans.report.JasperReportBean;
import cn.easyplatform.entities.beans.table.TableBean;
import cn.easyplatform.entities.beans.table.TableField;
import cn.easyplatform.entities.beans.task.TaskBean;
import cn.easyplatform.entities.transform.TransformerFactory;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.lang.stream.StringOutputStream;
import cn.easyplatform.studio.StudioApp;
import cn.easyplatform.studio.cmd.biz.UpdateCmd;
import cn.easyplatform.studio.cmd.entity.GetAndLockEntityCmd;
import cn.easyplatform.studio.cmd.entity.SaveCmd;
import cn.easyplatform.studio.cmd.entity.UnlockCmd;
import cn.easyplatform.studio.cmd.link.*;
import cn.easyplatform.studio.cmd.taskLink.*;
import cn.easyplatform.studio.context.Contexts;
import cn.easyplatform.studio.utils.StringUtil;
import cn.easyplatform.studio.utils.StudioUtil;
import cn.easyplatform.studio.vos.FieldVo;
import cn.easyplatform.studio.vos.GetEntityVo;
import cn.easyplatform.studio.vos.LinkVo;
import cn.easyplatform.studio.vos.TypeLinkVo;
import cn.easyplatform.studio.web.action.CreateAction;
import cn.easyplatform.studio.web.layout.WorkbenchController;
import cn.easyplatform.type.EntityType;
import cn.easyplatform.type.FieldType;
import cn.easyplatform.type.SubType;
import cn.easyplatform.web.ext.cmez.CMeditor;
import org.zkoss.util.resource.Labels;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zul.Messagebox;
import org.zkoss.zul.Tab;

import java.util.ArrayList;
import java.util.List;

/**
 * @author <a href="mailto:shiny_vc@163.com">陈云亮</a> <br/>
 * @since 2.0.0 <br/>
 */
public abstract class AbstractEntityEditor<T extends BaseEntity> extends
        AbstractEditor implements EntityEditor<T>, EventListener<Event> {

    protected T entity;

    protected CMeditor source; //源码编辑框

    protected char code;

    protected Tab tab;

    private boolean dirty;

    public CreateAction.OnSuccessListener successListener;

    private LinkVo originalVo;
    /**
     * @param workbench
     */
    public AbstractEntityEditor(WorkbenchController workbench, T entity,
                                char code) {
        super(workbench);
        this.entity = entity;
        if (code != 'R') {
            String name = entity.getSubType().equals(SubType.JOB.getName()) ? "jobEdit" : entity.getType().toLowerCase() + "Edit";
            if (!Contexts.getUser().isAuthorized(name))
                code = 'R';
        }
        if(StudioApp.isCloudDeployment()){
            if (entity.getId().toLowerCase().startsWith("sys".toLowerCase())){
                code = 'R';
            }
        }
        this.code = code;
    }

    /*
     * 生成编辑器
     */
    @Override
    public void create(Object... args) {
        tab = new Tab(entity.getId());
        tab.setValue(this);
        if (Strings.isBlank(entity.getDescription()))
            tab.setTooltiptext(entity.getName());
        else
            tab.setTooltiptext(entity.getName() + "-" + entity.getDescription());
        tab.setClosable(true);
        tab.setSelected(true);
        tab.addEventListener(Events.ON_CLOSE, this);
        LinkVo vo = StudioApp.execute(new GetSimpleLinkCmd(entity.getId()));
        if (vo != null && code != 'C' && code != 'R' && LinkVo.VIRTUALSTATUS.equals(vo.getStatus())) {
            vo.setStatus(LinkVo.ENTITYSTATUS);
            StudioApp.execute(new UpdateLinkCmd(vo));
        }
        if (EntityType.TASK.getName().equals(entity.getType())) {
            TypeLinkVo typeLinkVo = StudioApp.execute(new GetSimpleTaskLinkCmd(entity.getId()));
            TaskBean bean = (TaskBean) entity;
            String mainString = "Main".equals(bean.getSubType()) ? TypeLinkVo.ROOTENTITYID: TypeLinkVo.NODEENTITYID;
            if (typeLinkVo != null && code != 'C' && code != 'R' && mainString.equals(typeLinkVo.isRoot()) == false) {
                typeLinkVo.setRoot(mainString);
                StudioApp.execute(new UpdateTaskLinkCmd(typeLinkVo));
            }
        }

        if (code != 'R') {
            if (EntityType.TASK.getName().equals(entity.getType())) {
                if (StudioApp.execute(new GetLinkCmd(entity.getId())).size() == 0) {
                    LinkVo rootVo = new LinkVo(entity.getId(), null, entity.getName(), entity.getType(), entity.getSubType(),
                            entity.getDescription(), null, null, null, null, code == 'C' ? "V":"E");
                    StudioApp.execute(new AddLinkCmd(rootVo));
                    boolean isMain = SubType.Main.getName().equals(entity.getSubType());
                    StudioApp.execute(new AddTaskLinkCmd(entity.getId(), null, isMain));
                }
                originalVo = StudioApp.execute(new GetLinkCmd(entity.getId())).get(0);
            } else {
                List<LinkVo> links = StudioApp.execute(new GetLinkCmd(entity));
                if (links != null && links.size() != 0)
                    originalVo = links.get(0);
            }

            tab.setAutoClose(false);
            dirty = code == 'C'
                    && !entity.getType().equals(EntityType.BPM.getName());
            //tab.setIconSclass(WebUtils.getIconSclass(entity.getType(), entity.getSubType()));
            //tab.setImage(WebUtils.getImage(entity.getType(), entity.getSubType()));
            if (this instanceof CodeEditor) {
                workbench.getHeader().setMenuitem(2);
                workbench.getFunctionBar().setMenuitem(2);
                workbench.getShortcutKeyBar().setMenuitem(2);
            }
            else {
                workbench.getHeader().setMenuitem(1);
                workbench.getFunctionBar().setMenuitem(1);
                workbench.getShortcutKeyBar().setMenuitem(1);
            }
            if (isDirty())
                tab.setLabel("*" + entity.getId());
        } else
            tab.setImage("~./images/lock.gif");
        // tab.setIconSclass("z-icon-lock");
        workbench.setDisabled("save", !dirty);
    }

    /*
     * 删除
     */
    @Override
    public void delete() {
        if (code == 'C') {
            close();
        } else if (code == 'U') {
            Messagebox.show(
                    Labels.getLabel("message.confirm",
                            new String[]{Labels.getLabel("button.delete")}),
                    Labels.getLabel("message.title.confirm"), Messagebox.YES
                            | Messagebox.NO, Messagebox.QUESTION,
                    new EventListener<Event>() {
                        @Override
                        public void onEvent(Event event) throws Exception {
                            if (event.getName().equals(Messagebox.ON_YES)) {
                                EntityInfo ei = new EntityInfo();
                                ei.setId(entity.getId());
                                ei.setStatus('D');
                                StudioApp.execute(new SaveCmd(ei));
                                close();
                                workbench.getEntityBar().refreshEntity();
                                workbench.getEntityWindowBar().refreshEntity();

                                //更新link
                                LinkVo deleteVo = new LinkVo();
                                deleteVo.setEntityId(ei.getId());
                                StudioApp.execute(new DeleteLinkCmd(deleteVo));
                                StudioApp.execute(new DeleteTaskLinkCmd(new TypeLinkVo(deleteVo.getEntityId(), null, null)));
                                //更新跟删除有关的关系
                                List<LinkVo> updateVos = StudioApp.execute(new GetChildrenLinkCmd(deleteVo.getEntityId()));
                                if (updateVos != null && updateVos.size() > 0) {
                                    for (LinkVo updateVo: updateVos) {
                                        String childrenStr = StringUtil.removeStringToListString(updateVo.getChildrenEntityId(),
                                                ",", deleteVo.getEntityId());
                                        updateVo.setChildrenEntityId(childrenStr);
                                    }
                                    StudioApp.execute(new UpdateLinkListCmd(updateVos, deleteVo.getEntityId()));
                                }
                                List<TypeLinkVo> updateLinkVos = StudioApp.execute(new GetChildrenTaskLinkCmd(deleteVo.getEntityId()));
                                if (updateLinkVos != null && updateLinkVos.size() > 0) {
                                    for (TypeLinkVo updateVo: updateLinkVos) {
                                        String childrenStr = StringUtil.removeStringToListString(updateVo.getChildrenEntityId(),
                                                ",", deleteVo.getEntityId());
                                        updateVo.setChildrenEntityId(childrenStr);
                                    }
                                    StudioApp.execute(new UpdateTaskLinkListCmd(updateLinkVos, deleteVo.getEntityId()));
                                }
                            }
                        }
                    });
        }
    }

    /*
     * 保存
     */
    @Override
    public boolean save() {
        if (code == 'R')
            return true;
        boolean validate = validate();
        if (validate) {
            saveEntity(entity, code);
            tab.setLabel(entity.getId());
            dirty = false;
            workbench.setDisabled("save", true);
            boolean isChangeStatus = false;
            if (code == 'C') {
                code = 'U';
                isChangeStatus = true;
            }
            if (successListener != null) {
                successListener.saveEntity(entity);
                successListener = null;
            }
            else if (originalVo != null)
            {
                if (Strings.isBlank(originalVo.getEntityId()) == false && originalVo.getEntityId().equals(entity.getId()))
                {
                    boolean isNeedUpdate = false;
                    if (Strings.isBlank(originalVo.getEntityName()) || originalVo.getEntityName().equals(entity.getName())  == false) {
                        isNeedUpdate = true;
                        originalVo.setEntityName(entity.getName());
                    }

                    if (Strings.isBlank(originalVo.getEntityDesp()) || originalVo.getEntityDesp().equals(entity.getDescription()) == false) {
                        isNeedUpdate = true;
                        originalVo.setEntityDesp(entity.getDescription());
                    }

                    if (Strings.isBlank(originalVo.getEntityType()) || originalVo.getEntityType().equals(entity.getType()) == false) {
                        isNeedUpdate = true;
                        originalVo.setEntityType(entity.getType());
                    }

                    if (Strings.isBlank(originalVo.getEntitySubType()) || originalVo.getEntitySubType().equals(entity.getSubType()) == false) {
                        isNeedUpdate = true;
                        originalVo.setEntitySubType(entity.getSubType());
                    }

                    if (LinkVo.VIRTUALSTATUS.equals(originalVo.getStatus())) {
                        isNeedUpdate = true;
                        originalVo.setStatus(LinkVo.ENTITYSTATUS);
                    }

                    if (isChangeStatus) {
                        isNeedUpdate = true;
                        originalVo.setStatus(LinkVo.ENTITYSTATUS);
                    }
                    if (isNeedUpdate == true)
                        StudioApp.execute(new UpdateLinkCmd(originalVo, true));
                }
            }
        }
        return validate;
    }

    public void saveEntity(T saveEntity, char saveCode) {
        StudioUtil.save(saveEntity, saveCode);
        if (saveEntity instanceof TableBean) {
            TableBean tableBean = (TableBean) saveEntity;
            List<FieldVo> params = new ArrayList<FieldVo>();
            if (saveCode == 'C' || saveCode == 'U') {
                if (saveCode == 'U') {
                    StudioApp.execute(new UpdateCmd(entity.getSubType(), "delete from sys_table_field_info where table1id=" + "'" + tableBean.getId() + "'", null));
                }
                String insertSql = "INSERT INTO sys_table_field_info(table1id,table1name,table1description,field1name,field1type,field1length,field1decimal,field1description,field1notnull,field1orderNo,field1acc,field1defaultvalue) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)";
                int i = 1;
                for (TableField tf : tableBean.getFields()) {
                    params.clear();
                    params.add(new FieldVo(FieldType.VARCHAR, tableBean.getId()));//表名
                    params.add(new FieldVo(FieldType.VARCHAR, tableBean.getName()));//表中文名
                    params.add(new FieldVo(FieldType.VARCHAR, tableBean.getDescription()));//表描述
                    params.add(new FieldVo(FieldType.VARCHAR, tf.getName()));//字段名
                    params.add(new FieldVo(FieldType.VARCHAR, tf.getType()));//字段类型
                    params.add(new FieldVo(FieldType.VARCHAR, tf.getLength()));//字段长度
                    params.add(new FieldVo(FieldType.VARCHAR, tf.getDecimal()));//小数位
                    params.add(new FieldVo(FieldType.VARCHAR, tf.getDescription()));//字段说明
                    params.add(new FieldVo(FieldType.VARCHAR, tf.isNotNull()));//是否可为空
                    params.add(new FieldVo(FieldType.VARCHAR, i + ""));//字段顺序
                    params.add(new FieldVo(FieldType.VARCHAR, tf.getAcc()));//关联字段
                    params.add(new FieldVo(FieldType.VARCHAR, tf.getDefaultValue()));//字段默认值
                    StudioApp.execute(new UpdateCmd(entity.getSubType(), insertSql, params));
                    i++;
                }
            }
        }
    }

    /*
     * 数据已更改
     */
    protected void setDirty() {
        if (code != 'R') {
            dirty = true;
            workbench.setDisabled("save", false);
            workbench.setDisabled("saveAll", false);
            tab.setLabel("*" + entity.getId());
            refreshSource();
        }
    }

    /**
     * 刷新源代码
     */
    protected void refreshSource() {
        if (entity instanceof ContentEntity) {
            source.setValue(((ContentEntity) entity).getContent());
        } else {
            StringBuilder sb = new StringBuilder();
            StringOutputStream os = new StringOutputStream(sb);
            TransformerFactory.newInstance().transformToXml(entity, os);
            source.setValue(sb.toString());
            os = null;
            sb = null;
        }
    }

    /*
     * 唯一id
     */
    @Override
    public String getId() {
        return entity.getId();
    }

    /*
     * 是否已更改
     */
    @Override
    public boolean isDirty() {
        if (code == 'R')
            return false;
        return dirty;
    }

    /*
     * 关闭编辑器
     */
    @Override
    public void close() {
        tab.setAutoClose(true);
        tab.close();
        if (code != 'R')
            StudioApp.execute(new UnlockCmd(entity.getId()));
    }

    @Override
    public void onEvent(Event event) throws Exception {
        if (event.getTarget() == tab) {// Events.ON_CLOSE
            if (Contexts.getUser().isAuthorized("save")&&dirty) {
                Messagebox.show(Labels.getLabel("message.confirm.close"),
                        Labels.getLabel("message.title.confirm"),
                        Messagebox.YES | Messagebox.NO | Messagebox.CANCEL,
                        Messagebox.QUESTION, new EventListener<Event>() {
                            @Override
                            public void onEvent(Event event) throws Exception {
                                if (event.getName().equals(Messagebox.ON_YES)
                                        || event.getName().equals(
                                        Messagebox.ON_NO)) {
                                    if (event.getName().equals(
                                            Messagebox.ON_YES)) {
                                        if (save())
                                            close();
                                    } else
                                        close();
                                }
                            }
                        });
            } else {
                close();
            }
        } else
            dispatch(event);
    }

    /**
     * @param ref
     * @param id
     */
    protected void openEditor(Component ref, String id) {
        if (!id.startsWith("$") && workbench.checkEditor(id) == null) {
            GetEntityVo ge = StudioApp
                    .execute(ref, new GetAndLockEntityCmd(id));
            if (ge != null) {
                char code = 'U';
                if (!Strings.isBlank(ge.getLockUser())) {
                    code = 'R';
                    Messagebox.show(
                            Labels.getLabel("enitiy.locked",
                                    new String[]{ge.getLockUser()}),
                            Labels.getLabel("message.title.error"),
                            Messagebox.OK, Messagebox.INFORMATION);
                }
                String table = null;
                if (entity instanceof BatchBean)
                    table = ((BatchBean) entity).getTargetTable();
                else if (entity instanceof ListBean)
                    table = ((ListBean) entity).getTable();
                else if (entity instanceof PageBean)
                    table = ((PageBean) entity).getTable();
                else if (entity instanceof JasperReportBean)
                    table = ((JasperReportBean) entity).getTable();
                else if (entity instanceof TaskBean)
                    table = ((TaskBean) entity).getTable();
                if (Strings.isBlank(table))
                    workbench.createEditor(ge.getEntity(), code);
                else
                    workbench.createEditor(ge.getEntity(), code, table);
            }
        }
    }

    @Override
    public T getEntity() {
        return entity;
    }

    @Override
    public void setTheme(String theme) {
        this.source.setTheme(theme);
    }

    /**
     * 绘制概略图
     */
    protected void redraw() {
    }

    ;

    /**
     * 分发事件
     *
     * @param evt
     */
    protected abstract void dispatch(Event evt);

    /**
     * 检查内容的合法性
     *
     * @return
     */
    protected abstract boolean validate();
}
